package com.jingdun.sport.pay

import com.alibaba.fastjson.JSON
import com.alipay.api.AlipayApiException
import com.alipay.api.internal.util.AlipaySignature
import lombok.Data
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Controller
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseBody
import java.util.*
import java.util.concurrent.Executors
import javax.servlet.http.HttpServletRequest
import kotlin.collections.HashMap
import kotlin.math.pow

@Data
class AlipayNotifyParam {
    //    通知时间
    var notify_time: Date? = null

    //    通知类型
    var notify_type: String? = null

    //    通知校验ID
    var notify_id: String? = null
    var charset: String? = null
    var version: String? = null

    //    签名类型
    var sign_type: String? = null

    //    签名
    var sign: String? = null

    //    授权方的app_id
    var auth_app_id: String? = null

    //    支付宝交易号
    var trade_no: String? = null

    //    开发者的app_id
    var app_id: String? = null

    //    商户订单号
    var out_trade_no: String? = null

    //    商户业务号
    var out_biz_no: String? = null

    //    买家支付宝用户号
    var buyer_id: String? = null

    //    卖家支付宝用户号
    var seller_id: String? = null

    //    交易状态
    var trade_status: String? = null

    //    订单金额
    var total_amount: String? = null

    //    实收金额
    var receipt_amount: String? = null

    //    开票金额
    var invoice_amount: String? = null

    //    付款金额
    var buyer_pay_amount: String? = null

    //    集分宝金额
    var point_amount: String? = null

    //    总退款金额
    var refund_fee: String? = null

    //    订单标题
    var subject: String? = null

    //    商品描述
    var body: String? = null

    // 交易创建时间
    var gmt_create: String? = null

    //    交易付款时间
    var gmt_payment: String? = null

    //    交易退款时间
    var gmt_refund: String? = null

    //    交易结束时间
    var gmt_close: String? = null

    //    支付金额信息
    var fund_bill_list: String? = null

    //    优惠券信息
    var voucher_detail_list: String? = null

    //    回传参数
    var passback_params: String? = null
}

enum class AlipayTradeStatus(val status: String) {
    //    交易创建，等待买家付款
    WAIT_BUYER_PAY("WAIT_BUYER_PAY"),  //    未付款交易超时关闭，或支付完成后全额退款
    TRADE_CLOSED("WAIT_BUYER_PAY"),  //    交易支付成功
    TRADE_SUCCESS("TRADE_SUCCESS"),  //    交易结束，不可退款
    TRADE_FINISHED("TRADE_FINISHED");
}

data class PayResult(
        val tradeNo: String,
        val amount: Long,
        val amountScale: Int
)

interface PayCallback {
    fun succeed(result: PayResult)
    fun failed(result: PayResult)
}

@Controller
class AlipayCallbackController {
    @Autowired
    private lateinit var alipayConfig: Alipay

    @Autowired
    private lateinit var callbacks: List<PayCallback>

    private val executorService = Executors.newFixedThreadPool(20)

    // 将request中的参数转换成Map
    private fun convertRequestParamsToMap(request: HttpServletRequest): Map<String, String> {
        val retMap: MutableMap<String, String> = HashMap()
        val entrySet: Set<Map.Entry<String, Array<String>>> = request.parameterMap.entries
        for ((name, values) in entrySet) {
            val valLen = values.size
            retMap[name] = when {
                valLen == 1 -> {
                    values[0]
                }
                valLen > 1 -> {
                    val sb = StringBuilder()
                    for (`val` in values) {
                        sb.append(",").append(`val`)
                    }
                    sb.toString().substring(1)
                }
                else -> {
                    ""
                }
            }
        }
        return retMap
    }

    private fun check(params: Map<String, String>) {
        if (params["app_id"] != alipayConfig.appid) {
            throw AlipayApiException("app_id不一致")
        }
    }

    private fun buildAlipayNotifyParam(params: Map<String, String>): AlipayNotifyParam {
        val json = JSON.toJSONString(params)
        return JSON.parseObject(json, AlipayNotifyParam::class.java)
    }

    @RequestMapping("alipay_callback")
    @ResponseBody
    fun callback(request: HttpServletRequest): String {
        val params: Map<String, String> = convertRequestParamsToMap(request) // 将异步通知中收到的待验证所有参数都存放到map中

        val paramsJson = JSON.toJSONString(params)
        return try {
            // 调用SDK验证签名
            val signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.alipay_public_key,
                    alipayConfig.charset, alipayConfig.signtype)
            if (signVerified) {
                // 按照支付结果异步通知中的描述，对支付结果中的业务内容进行1\2\3\4二次校验，校验成功后在response中返回success，校验失败返回failure
                check(params)
                // 另起线程处理业务
                executorService.execute(Runnable {
                    val param: AlipayNotifyParam = buildAlipayNotifyParam(params)
                    val tradeStatus: String? = param.trade_status
                    // 支付成功
                    if (tradeStatus == AlipayTradeStatus.TRADE_SUCCESS.status || tradeStatus == AlipayTradeStatus.TRADE_FINISHED.status) {
                        for (callback in callbacks) {
                            // 处理支付成功逻辑
                            try {
                                val amount = param.total_amount!!
                                val scaledAmount = amount.filter { it != '.' }.toLong()
                                val pointPosition = amount.indexOf('.')
                                val scalePower = amount.length - 1 - pointPosition
                                val scale = 10.0.pow(scalePower.toDouble()).toInt()
                                callback.succeed(PayResult(param.out_trade_no!!, scaledAmount, scale))
                            } catch (e: Exception) {
                            }
                        }
                    } else {
                    }
                })
                // 如果签名验证正确，立即返回success，后续业务另起线程单独处理
                // 业务处理失败，可查看日志进行补偿，跟支付宝已经没多大关系。
                "success"
            } else {
                "failure"
            }
        } catch (e: AlipayApiException) {
            "failure"
        }
    }
}